μΉ μ ν리μΌμ΄μ μμ 볡μλ ₯ μκ³ μ΄μ΄λ°κΈ° κ°λ₯ν λ€μ΄λ‘λλ₯Ό ꡬννμΈμ. μ΄ μ’ ν© κ°μ΄λλ λ°±κ·ΈλΌμ΄λ νμΉ API, μλΉμ€ μ컀, κ·Έλ¦¬κ³ λ€νΈμν¬ μ€λ¨ μμλ μνν λμ©λ νμΌ μ μ‘μ μν μ€μ ꡬν λ°©λ²μ λ€λ£Ήλλ€.
νλ°νΈμλ λ°±κ·ΈλΌμ΄λ νμΉ λ§μ€ν°νκΈ°: 볡μλ ₯ μκ³ μ΄μ΄λ°κΈ° κ°λ₯ν λ€μ΄λ‘λ ꡬμΆ
μ μ λ μ°κ²°λλ μΈμμμ μΉμ λ μ΄μ μ μ μΈ λ¬Έμλ§μ μν 곡κ°μ΄ μλλλ€. κ³ νμ§ λΉλμ€ μ½ν μΈ λΆν° 볡μ‘ν λΉμ¦λμ€ μννΈμ¨μ΄, λͺ°μ ν κ²μμ μ΄λ₯΄κΈ°κΉμ§ λͺ¨λ κ²μ μ 곡νλ νλΆνκ³ μνΈμμ©μ μΈ μ ν리μΌμ΄μ μ μν νλ«νΌμ λλ€. μ΄λ¬ν λ°μ μ μ μΈκ³ κ°λ°μλ€μ΄ μ§λ©΄ν΄μΌ νλ μ€μν κ³Όμ λ₯Ό κ°μ Έμ΅λλ€. λ°λ‘ μ’ μ’ μ λ’°ν μ μλ λ€νΈμν¬λ₯Ό ν΅ν΄ λμ©λ νμΌμ μμ μ μΌλ‘ μ μ‘νλ κ²μ λλ€. μμΈμ ν΅κ·Ό μ΄μ°¨μ μλ μ¬μ©μλ , λ¨λ―Έμ μ골 μ§μμ μλ νμμ΄λ , λλ°μ΄μ λΆμμ ν νΈν Wi-Fiμ μ°κ²°λ μ λ¬Έκ°λ , μ°κ²°μ΄ λκΈ°λ©΄ λ€μ΄λ‘λκ° μ€ν¨νκ³ μ¬μ©μλ μ’μ νλ©° κ²½νμ λ§κ°μ§ μ μμ΅λλ€. λ°λ‘ μ΄ μ§μ μμ λ°±κ·ΈλΌμ΄λ νμΉ API(Background Fetch API)κ° νλλ₯Ό λ°κΎΈλ ν΄κ²°μ± μΌλ‘ λ±μ₯ν©λλ€.
`fetch()`λ `XMLHttpRequest`μ κ°μ μ ν΅μ μΈ λ°©λ²μ κ°λ ₯νμ§λ§, λ³Έμ§μ μΌλ‘ μΉ νμ΄μ§μ μλͺ μ£ΌκΈ°μ λ¬Άμ¬ μμ΅λλ€. μ¬μ©μκ° νμ λ«κ±°λ λ€λ₯Έ νμ΄μ§λ‘ μ΄λνλ©΄ λ€μ΄λ‘λλ μ’ λ£λ©λλ€. νμ΄μ§ μΈμ μ λμ΄ μμ‘΄ν μ μλ λ΄μ₯λ λ©μ»€λμ¦μ΄ μμ΅λλ€. λ°±κ·ΈλΌμ΄λ νμΉ APIλ μ΄λ¬ν ν¨λ¬λ€μμ κ·Όλ³Έμ μΌλ‘ λ°κΏλλ€. μΉ μ ν리μΌμ΄μ μ΄ λμ©λ λ€μ΄λ‘λ(λ° μ λ‘λ) μμ μ λΈλΌμ°μ μ체μ μμν μ μκ² ν΄μ£Όλ©°, λΈλΌμ°μ λ λ¨μΌ νκ³Ό λ 립μ μΌλ‘ λ°±κ·ΈλΌμ΄λμμ μ μ‘μ κ΄λ¦¬ν©λλ€. μ΄λ μ¬μ©μκ° νμ΄μ§λ₯Ό λ«μλ λ€μ΄λ‘λκ° κ³μλ μ μμμ μλ―Ένλ©°, λ μ€μνκ²λ λ€νΈμν¬ μ°κ²° μνκ° λ³κ²½λ λ μλμΌλ‘ μΌμ μ€μ§λκ³ μ¬κ°λ μ μλ€λ κ²μ λλ€. μ΄κ²μ΄ λ°λ‘ μΉμμ μ§μ μΌλ‘ 볡μλ ₯ μκ³ λ€μ΄ν°λΈμ κ°μ λ€μ΄λ‘λ κ²½νμ ꡬμΆνλ μ΄μ μ λλ€.
λ°±κ·ΈλΌμ΄λ νμΉ APIλ 무μμΈκ°? κΈλ‘λ² κ΄μ μμμ μ΄ν΄
ν΅μ¬μ μΌλ‘ λ°±κ·ΈλΌμ΄λ νμΉ APIλ λκ·λͺ¨ λ€νΈμν¬ μμ²μ λΈλΌμ°μ μμ§μ μμνλλ‘ μ€κ³λ μ΅μ μΉ νμ€μ λλ€. μ΄λ κ°λ°μκ° μ ν리μΌμ΄μ μ 보μ΄λ μ°½μ μλͺ μ λμ΄μ μ§μλλ λ€μ΄λ‘λλ μ λ‘λλ₯Ό μμν μ μλλ‘ νμ μ€μ΄μ€λλ€. μ΄κ²μ μ¬μν νΈμμ±μ΄ μλλΌ, λ κ²¬κ³ νκ³ μ λ₯ν μΉμ μν κΈ°λ° κΈ°μ μ λλ€.
κΈλ‘λ² κ΄μ μμ κ·Έ μν₯μ μκ°ν΄ 보μμμ€. μΈκ³ μ¬λ¬ μ§μμμ κ³ μμ μμ μ μΈ μΈν°λ·μ λΉμ°ν κ²μ΄ μλλΌ μ¬μΉμ λλ€. λͺ¨λ°μΌ λ°μ΄ν°λ λΉμΈκ³ μ¬μ©λμ λ°λΌ κ³ΌκΈλ μ μμ΅λλ€. μ ν리μΌμ΄μ μ΄ μ§μ μΌλ‘ κΈλ‘λ²νλλ €λ©΄ μ΄λ¬ν λ€μν λ€νΈμν¬ μ‘°κ±΄μ κ³ λ €ν΄μΌ ν©λλ€. λ°±κ·ΈλΌμ΄λ νμΉλ ννμ±μ κ°λ₯νκ² νλ κΈ°μ μ λλ€. μ°κ²°μ΄ κ°νμ μΈ μ§μμ μ¬μ©μκ° κ΅μ‘μ© λΉλμ€λ μ€μν μννΈμ¨μ΄ μ λ°μ΄νΈ λ€μ΄λ‘λλ₯Ό μμνκ³ , μ°κ²°μ΄ νμ©νλ λλ‘ λ°±κ·ΈλΌμ΄λμμ μλ£λ κ²μ΄λΌκ³ μ λ’°νλ©°, μ€ν¨ν νμΌμ λ€μ λ€μ΄λ‘λνλ λ° μμ€ν λ°μ΄ν°λ₯Ό λλΉνμ§ μλλ‘ ν΄μ€λλ€.
λ°±κ·ΈλΌμ΄λ νμΉμ μ£Όμ μ΄μ
- 볡μλ ₯ λ° μ΄μ΄λ°κΈ°: μ΄κ²μ΄ κ°μ₯ ν΅μ¬μ μΈ κΈ°λ₯μ λλ€. λΈλΌμ°μ μ κΈ°λ³Έ λ€μ΄λ‘λ κ΄λ¦¬μλ λ€νΈμν¬ μ€λ¨μ μ°μνκ² μ²λ¦¬ν©λλ€. μ°κ²°μ΄ λκΈ°λ©΄ λ€μ΄λ‘λλ μΌμ μ€μ§λ©λλ€. μ°κ²°μ΄ 볡μλλ©΄ μ€λ¨λ μ§μ λΆν° μλμΌλ‘ μ¬κ°λ©λλ€. μ΄λ HTTP `Range` ν€λλ₯Ό μ²λ¦¬νκΈ° μν 볡μ‘ν JavaScript λ‘μ§ μμ΄ λ°μν©λλ€.
- μ€νλΌμΈ μ§μμ±: λ€μ΄λ‘λλ λΈλΌμ°μ νλ‘μΈμ€μ μν΄ κ΄λ¦¬λκ³ μλΉμ€ μ컀μ μν΄ μ²λ¦¬λλ―λ‘ μ΄λ € μλ νμ λ¬Άμ¬ μμ§ μμ΅λλ€. μ¬μ©μλ λ€μ΄λ‘λλ₯Ό μμνκ³ , λ ΈνΈλΆμ λ«κ³ , μ§μΌλ‘ ν΅κ·Όν ν λ€μ μ΄μμ λ λ€μ΄λ‘λκ° μλ£λμκ±°λ μ§νλ κ²μ λ°κ²¬ν μ μμ΅λλ€.
- 리μμ€ ν¨μ¨μ±: λΈλΌμ°μ λ 리μμ€ μ¬μ©μ μ΅μ νν μ μλ κ°μ₯ μ’μ μμΉμ μμ΅λλ€. Wi-Fi μ°κ²°μ νμ©νμ¬ μ μ‘μ μμ½νκ³ λͺ¨λ°μΌ λ°μ΄ν°λ₯Ό μ μ½νλ©°, λͺ¨λ λͺ¨λ°μΌ μ¬μ©μμκ² μ€μν λ¬Έμ μΈ λ°°ν°λ¦¬ μλͺ μ μ΅μ ννκΈ° μν΄ νλ‘μΈμ€λ₯Ό κ΄λ¦¬ν μ μμ΅λλ€.
- ν΅ν©λ μ¬μ©μ κ²½ν: λΈλΌμ°μ λ μ§ν μ€μΈ λ€μ΄λ‘λμ λν΄ λ€μ΄ν°λΈ μμ€ν μμ€μ μ¬μ©μ μΈν°νμ΄μ€λ₯Ό μ 곡ν μ μμ΅λλ€. μ¬μ©μλ λ€μ΄ν°λΈ μ ν리μΌμ΄μ μμ λ€μ΄λ‘λλ₯Ό κ΄λ¦¬νλ κ²κ³Ό λμΌν μμΉμμ μ΄λ¬ν μΉ λ€μ΄λ‘λλ₯Ό λ³΄κ³ κ΄λ¦¬νμ¬ μννκ³ μ΅μν κ²½νμ λ§λλλ€. μ¬κΈ°μλ μ§νλ₯ , μλ£ λ° μ€ν¨μ λν μλ¦Όμ΄ ν¬ν¨λ©λλ€.
ν΅μ¬ κ΅¬μ± μμ: μλΉμ€ μ컀μ BackgroundFetchManager
λ°±κ·ΈλΌμ΄λ νμΉλ₯Ό μ΄ν΄νλ €λ©΄ λ¨Όμ λ κ°μ§ μ£Όμ κ΅¬μ± μμμ μ΅μν΄μ ΈμΌ ν©λλ€. μ΄λ€μ ν¨κ» μλν©λλ€. νλλ μΉ νμ΄μ§μμ μμ²μ μμνκ³ , λ€λ₯Έ νλλ λ°±κ·ΈλΌμ΄λμμ κ²°κ³Όλ₯Ό κ΄λ¦¬ν©λλ€.
μ¨μ μ£Όμ: μλΉμ€ μ컀
μλΉμ€ μ컀(Service Worker)λ μΉ μ컀μ ν μ’ λ₯λ‘, λ³Έμ§μ μΌλ‘ λΈλΌμ°μ κ° μΉ νμ΄μ§μ μμ ν λΆλ¦¬λ λ°±κ·ΈλΌμ΄λμμ μ€ννλ JavaScript μ€ν¬λ¦½νΈμ λλ€. νλ‘κ·Έλλ° κ°λ₯ν λ€νΈμν¬ νλ‘μ μν μ νμ¬ λ€νΈμν¬ μμ²μ κ°λ‘μ±κ³ μ²λ¦¬νλ©°, μΊμλ₯Ό κ΄λ¦¬νκ³ νΈμ μλ¦Όμ νμ±νν©λλ€. λ 립μ μΌλ‘ μ€νλκΈ° λλ¬Έμ μΉμ¬μ΄νΈκ° λΈλΌμ°μ νμ μ΄λ € μμ§ μμ λλ μμ μ μνν μ μμ΅λλ€. λ°±κ·ΈλΌμ΄λ νμΉμμ μλΉμ€ μ컀λ λ€μ΄λ‘λμ μ΅μ’ μ±κ³΅ λλ μ€ν¨λ₯Ό μμ νκ³ , κ²°κ³Ό νμΌμ μ²λ¦¬νλ©°, UIλ₯Ό μ λ°μ΄νΈνκ±°λ μ€νλΌμΈ μ¬μ©μ μν΄ μμ°μ μΊμνλ μ§μμ μΈ νκ²½μ λλ€.
μ§νμ: BackgroundFetchManager
`BackgroundFetchManager`λ λ©μΈ μΉ νμ΄μ§μ JavaScriptμμ μ κ·Όν μ μλ μΈν°νμ΄μ€λ‘, λ°±κ·ΈλΌμ΄λ νμΉλ₯Ό μμνκ³ κ΅¬μ±νλ λ° μ¬μ©λ©λλ€. μλΉμ€ μ컀 λ±λ‘ κ°μ²΄λ₯Ό ν΅ν΄ μ κ·Όν©λλ€: `navigator.serviceWorker.ready.then(swReg => swReg.backgroundFetch)`. μ£Όμ λ©μλλ `fetch()`μ΄λ©°, ID, λ€μ΄λ‘λν νμΌ λͺ©λ‘, κ·Έλ¦¬κ³ μ΅μ μΈνΈλ₯Ό μΈμλ‘ λ°μ΅λλ€. μ΄ λ©μλλ μΆλ° μ νΈμ κ°μ΅λλ€. μΌλ¨ νΈμΆνλ©΄ λΈλΌμ°μ κ° μμ μ μΈκ³λ°κ³ μλΉμ€ μ컀λ κ²°μΉμ μμ κΈ°λ€λ¦½λλ€.
μ€μ©μ μΈ λ¨κ³λ³ ꡬν κ°μ΄λ
λμ©λ λΉλμ€ νμΌμ λν μ΄μ΄λ°κΈ° κ°λ₯ν λ€μ΄λ‘λλ₯Ό ꡬννλ κ³Όμ μ λ¨κ³λ³λ‘ μ΄ν΄λ³΄κ² μ΅λλ€. μ΄ μλ λ―Έκ΅μ λ―Έλμ΄ νλ«νΌμ΄λ , μΈλμ e-λ¬λ μ¬μ΄νΈλ , λ μΌμ κΈ°μ κ΅μ‘ ν¬νΈμ΄λ 보νΈμ μΌλ‘ μ μ© κ°λ₯ν©λλ€.
1λ¨κ³: λΈλΌμ°μ μ§μ νμΈ
λ€λ₯Έ μ΄λ€ μμ μ νκΈ° μ μ μ¬μ©μμ λΈλΌμ°μ κ° λ°±κ·ΈλΌμ΄λ νμΉ APIλ₯Ό μ§μνλμ§ νμΈν΄μΌ ν©λλ€. μ μ§μ ν₯μ(progressive enhancement)μΌλ‘ μλ €μ§ μ΄ κ΄νμ κ°μ₯ μ§λ³΄λ κΈ°λ₯μ μ¬μ©νμ§ λͺ»νλλΌλ λͺ¨λ μ¬μ©μμκ² κΈ°λ₯μ μΈ κ²½νμ 보μ₯ν©λλ€.
λ©μΈ μ ν리μΌμ΄μ μ€ν¬λ¦½νΈμμ `BackgroundFetchManager`μ μ‘΄μ¬ μ¬λΆλ₯Ό νμΈν©λλ€:
if ('BackgroundFetchManager' in self) { // APIκ° μ§μλλ―λ‘ ν₯μλ λ€μ΄λ‘λ λ²νΌμ νμν μ μμ΅λλ€ } else { // APIκ° μ§μλμ§ μμΌλ―λ‘ λ체 μλ¨(μ: νμ€ λ§ν¬)μ μ 곡ν©λλ€ }
2λ¨κ³: μλΉμ€ μ컀 λ±λ‘
λ°±κ·ΈλΌμ΄λ νμΉλ κ·Όλ³Έμ μΌλ‘ μλΉμ€ μ컀μ μμ‘΄ν©λλ€. νλ‘κ·Έλ μλΈ μΉ μ±(PWA)μ μν΄ μμ§ μλΉμ€ μμ»€κ° μλ€λ©΄ νλλ₯Ό λ§λ€κ³ λ±λ‘ν΄μΌ ν©λλ€. νλ‘μ νΈμ λ£¨νΈ λλ ν°λ¦¬μ `service-worker.js`λΌλ νμΌμ λ§λλλ€. κ·Έλ° λ€μ λ©μΈ JavaScript νμΌμμ λ±λ‘ν©λλ€:
async function registerServiceWorker() { if ('serviceWorker' in navigator) { try { const registration = await navigator.serviceWorker.register('/service-worker.js'); console.log('μλΉμ€ μμ»€κ° μ±κ³΅μ μΌλ‘ λ±λ‘λμμ΅λλ€:', registration); } catch (error) { console.error('μλΉμ€ μ컀 λ±λ‘μ μ€ν¨νμ΅λλ€:', error); } } } registerServiceWorker();
3λ¨κ³: νλ°νΈμλμμ λ°±κ·ΈλΌμ΄λ νμΉ μμνκΈ°
μ΄μ μ¬μ©μκ° λ²νΌμ ν΄λ¦νμ λ λ€μ΄λ‘λλ₯Ό μμνλ ν¨μλ₯Ό λ§λ€μ΄ λ³΄κ² μ΅λλ€. μ΄ ν¨μλ νμ± μλΉμ€ μ컀 λ±λ‘μ κ°μ Έμ¨ λ€μ `backgroundFetch.fetch()`λ₯Ό νΈμΆν©λλ€.
const downloadVideoButton = document.getElementById('download-video-btn'); downloadVideoButton.addEventListener('click', async () => { try { // μλΉμ€ μ컀 λ±λ‘ κ°μ Έμ€κΈ° const swReg = await navigator.serviceWorker.ready; // λ€μ΄λ‘λ μΈλΆ μ 보 μ μ const videoUrl = '/assets/large-course-video.mp4'; const videoFileSize = 250 * 1024 * 1024; // 250 MB // λ°±κ·ΈλΌμ΄λ νμΉ μμ const bgFetch = await swReg.backgroundFetch.fetch('course-video-download-01', [videoUrl], { title: 'λͺ¨λ 1: μΉ κ°λ° μ λ¬Έ', icons: [{ sizes: '192x192', src: '/images/icons/icon-192.png', type: 'image/png', }], downloadTotal: videoFileSize, } ); console.log('λ°±κ·ΈλΌμ΄λ νμΉκ° μμλμμ΅λλ€:', bgFetch); } catch (error) { console.error('λ°±κ·ΈλΌμ΄λ νμΉλ₯Ό μμν μ μμ΅λλ€:', error); } });
`swReg.backgroundFetch.fetch()` λ§€κ°λ³μλ₯Ό μμΈν μ΄ν΄λ³΄κ² μ΅λλ€:
- ID (`'course-video-download-01'`): μ΄ νΉμ λ€μ΄λ‘λ μμ μ μν κ³ μ ν λ¬Έμμ΄ μλ³μμ λλ€. λμ€μ μ΄ IDλ₯Ό μ¬μ©νμ¬ μμ μ μ°Έμ‘°νκ² λ©λλ€.
- μμ² (`[videoUrl]`): κ°μ Έμ¬ URLμ λ°°μ΄μ λλ€. νλμ κ·Έλ£Ήνλ μμ μΌλ‘ μ¬λ¬ νμΌμ λ€μ΄λ‘λν μ μμ΅λλ€.
- μ΅μ (`{...}`): λ€μ΄λ‘λλ₯Ό ꡬμ±νκΈ° μν κ°μ²΄μ λλ€. `title`κ³Ό `icons`λ λΈλΌμ°μ κ° λ€μ΄ν°λΈ UI μλ¦Όμ λ§λλ λ° μ¬μ©λ©λλ€. `downloadTotal`μ λͺ¨λ νμΌμ ν©μΉ μμ μ΄ ν¬κΈ°(λ°μ΄νΈ)μ λλ€. μ΄ κ°μ μ 곡νλ κ²μ λΈλΌμ°μ κ° μ νν μ§νλ₯ νμμ€μ νμνλ λ° μ€μν©λλ€.
4λ¨κ³: μλΉμ€ μ컀μμ μ΄λ²€νΈ μ²λ¦¬νκΈ°
λ€μ΄λ‘λκ° λΈλΌμ°μ μ μμλλ©΄ νλ°νΈμλ μ½λμ μν μ μΌλ¨ λλ©λλ€. λλ¨Έμ§ λ‘μ§μ `service-worker.js`μ μμΌλ©°, μμ μ΄ μλ£λκ±°λ μ€ν¨ν λ λΈλΌμ°μ μ μν΄ κΉ¨μ΄λ©λλ€.
λ κ°μ§ μ£Όμ μ΄λ²€νΈμΈ `backgroundfetchsuccess`μ `backgroundfetchfail`μ μμ ν΄μΌ ν©λλ€.
// service-worker.jsμμ self.addEventListener('backgroundfetchsuccess', (event) => { const bgFetch = event.registration; event.waitUntil(async function () { console.log(`λ°±κ·ΈλΌμ΄λ νμΉ '${bgFetch.id}'κ° μ±κ³΅μ μΌλ‘ μλ£λμμ΅λλ€.`); // λ€μ΄λ‘λν νμΌμ μ μ₯ν μΊμ μ΄κΈ° const cache = await caches.open('downloaded-assets-v1'); // λ€μ΄λ‘λλ λͺ¨λ νμΌ λ μ½λ κ°μ Έμ€κΈ° const records = await bgFetch.matchAll(); // κ° λ μ½λμ λν΄ μλ΅μ μΊμμ μ μ₯ const promises = records.map(async (record) => { const response = record.response.clone(); await cache.put(record.request, response); }); await Promise.all(promises); // μ ν μ¬ν: λ€μ΄λ‘λ μλ¦Όμ UI μ λͺ© μ λ°μ΄νΈ await event.updateUI({ title: 'λ€μ΄λ‘λ μλ£ λ° μ€λΉλ¨!' }); }()); }); self.addEventListener('backgroundfetchfail', (event) => { const bgFetch = event.registration; console.error(`λ°±κ·ΈλΌμ΄λ νμΉ '${bgFetch.id}'κ° μ€ν¨νμ΅λλ€.`); // μ ν μ¬ν: μ€ν¨λ₯Ό λ°μνλλ‘ UI μ λ°μ΄νΈ event.updateUI({ title: 'λ€μ΄λ‘λ μ€ν¨. λ€μ μλν΄ μ£ΌμΈμ.' }); });
μ±κ³΅ νΈλ€λ¬μμλ Cache Storageλ₯Ό μ΄κ³ , `bgFetch.matchAll()`μ μ¬μ©νμ¬ λ€μ΄λ‘λλ λͺ¨λ νμΌμ κ²μν λ€μ, κ° νμΌμ μΊμμ λ£μ΅λλ€. μ΄λ κ² νλ©΄ μΉ μ ν리μΌμ΄μ μμ λΉλμ€λ₯Ό μ€νλΌμΈμΌλ‘ μ¬μν μ μμ΅λλ€.
5λ¨κ³: μ§ν μν© λͺ¨λν°λ§ λ° μ¬μ©μ μνΈμμ©
νλ₯ν μ¬μ©μ κ²½νμ νΌλλ°±μ μ 곡νλ κ²μ ν¬ν¨ν©λλ€. μ¬μ©μκ° λΈλΌμ°μ μμ μ 곡νλ λ€μ΄λ‘λ μλ¦Όμ ν΄λ¦νλ©΄ μ ν리μΌμ΄μ μ κ΄λ ¨ νμ΄μ§λ‘ μ΄λμμΌμΌ ν©λλ€. μ΄λ μλΉμ€ μ컀μ `backgroundfetchclick` μ΄λ²€νΈλ₯Ό ν΅ν΄ μ²λ¦¬ν©λλ€.
// service-worker.jsμμ self.addEventListener('backgroundfetchclick', (event) => { const bgFetch = event.registration; if (bgFetch.id === 'course-video-download-01') { event.waitUntil( clients.openWindow('/downloads') ); } });
μ΄ μ½λλ μ¬μ©μκ° μ΄ νΉμ λ€μ΄λ‘λ μμ μ μλ¦Όμ ν΄λ¦νμ λ μΉμ¬μ΄νΈμ `/downloads` νμ΄μ§λ₯Ό μ΄λλ‘ λΈλΌμ°μ μ μ§μν©λλ€. ν΄λΉ νμ΄μ§μμ λ€μ΄λ‘λ μ§ν μν©μ΄λ μλ£λ λ€μ΄λ‘λ λͺ©λ‘μ νμν μ μμ΅λλ€.
μ΄μ΄λ°κΈ°μ λ§λ²: μ€μ λ‘ μ΄λ»κ² μλνλκ°?
λ°±κ·ΈλΌμ΄λ νμΉμ κ°μ₯ κ°λ ₯νκ³ μλ§λ κ°μ₯ μ€ν΄λ°λ μΈ‘λ©΄μ μλ μ΄μ΄λ°κΈ° κΈ°λ₯μ λλ€. νΉλ³ν μ½λλ₯Ό μμ±νμ§ μκ³ λ μ΄λ»κ² μλν κΉμ?
λ΅μ κ³ λλ‘ μ΅μ νλ μμ€ν μμ€ νλ‘μΈμ€μΈ λΈλΌμ°μ μ체μ λ€μ΄λ‘λ κ΄λ¦¬μμ μ± μμ μμνλ€λ κ²μ λλ€. λ°±κ·ΈλΌμ΄λ νμΉλ₯Ό μμν λ, λΉμ μ λ€νΈμν¬λ₯Ό ν΅ν΄ λ°μ΄νΈλ₯Ό μ§μ κ΄λ¦¬νλ κ²μ΄ μλλλ€. λΈλΌμ°μ κ° κ·Έ μν μ ν©λλ€.
λ€νΈμν¬ μ€λ¨ μ λ°μνλ μ΄λ²€νΈ μμλ λ€μκ³Ό κ°μ΅λλ€:
- μ¬μ©μκ° νμΌμ λ€μ΄λ‘λνλ μ€ κΈ°κΈ°μ λ€νΈμν¬ μ°κ²°μ΄ λκΉλλ€(μ: ν°λμ μ§μ ).
- λΈλΌμ°μ μ λ€μ΄λ‘λ κ΄λ¦¬μκ° λ€νΈμν¬ μ€ν¨λ₯Ό κ°μ§νκ³ μ μ‘μ μ°μνκ² μΌμ μ€μ§ν©λλ€. μ±κ³΅μ μΌλ‘ μμ λ λ°μ΄νΈ μλ₯Ό μΆμ ν©λλ€.
- λμ€μ μ¬μ©μμ κΈ°κΈ°κ° λ€νΈμν¬ μ°κ²°μ λ€μ μ»μ΅λλ€.
- λΈλΌμ°μ λ μλμΌλ‘ λ€μ΄λ‘λ μ¬κ°λ₯Ό μλν©λλ€. λμΌν νμΌμ λν΄ μλ²μ μλ‘μ΄ HTTP μμ²μ 보λ΄μ§λ§, μ΄λ²μλ `Range` ν€λλ₯Ό ν¬ν¨νμ¬ μλ²μ ν¨κ³Όμ μΌλ‘ "λλ μ΄λ―Έ 첫 'X' λ°μ΄νΈλ₯Ό κ°μ§κ³ μμΌλ, λ°μ΄νΈ 'X+1'λΆν° μμν΄μ λλ¨Έμ§λ₯Ό 보λ΄μ£ΌμΈμ."λΌκ³ μ립λλ€.
- μ¬λ°λ₯΄κ² ꡬμ±λ μλ²λ `206 Partial Content` μνλ‘ μλ΅νκ³ νμΌμ λλ¨Έμ§ λΆλΆμ μ€νΈλ¦¬λ°νκΈ° μμν©λλ€.
- λΈλΌμ°μ λ μ΄ μλ‘μ΄ λ°μ΄ν°λ₯Ό λΆλΆμ μΌλ‘ λ€μ΄λ‘λλ νμΌμ μΆκ°ν©λλ€.
μ΄ μ 체 νλ‘μΈμ€λ JavaScript μ½λμ ν¬λͺ νκ² μ§νλ©λλ€. μλΉμ€ μ컀λ νμΌμ΄ μμ ν λ€μ΄λ‘λλμ΄ μ±κ³΅μ μΌλ‘ ν©μ³μ‘κ±°λ, νλ‘μΈμ€κ° μ΅μ’ μ μΌλ‘ μ€ν¨(μ: νμΌμ΄ λ μ΄μ μλ²μ μμ)νμ λ 맨 λ§μ§λ§μλ§ μλ¦Όμ λ°μ΅λλ€. μ΄ μΆμνλ λ§€μ° κ°λ ₯νμ¬ κ°λ°μκ° λ³΅μ‘νκ³ κΉ¨μ§κΈ° μ¬μ΄ λ€μ΄λ‘λ μ¬κ° λ‘μ§μ ꡬμΆνλ λΆλ΄μμ λ²μ΄λκ² ν΄μ€λλ€.
κ³ κΈ κ°λ λ° κΈλ‘λ² μ¬μ©μλ₯Ό μν λͺ¨λ² μ¬λ‘
μ νν `downloadTotal` μ 곡νκΈ°
`downloadTotal` μ΅μ μ μμΌλ©΄ μ’μ κ² μ΄μμ λλ€. μ΄κ²μ΄ μμΌλ©΄ λΈλΌμ°μ λ λΆνμ μ μΈ μ§νλ₯ νμκΈ°(μ: νμ νλ μμ΄μ½)λ§ νμν μ μμ΅λλ€. μ΄κ²μ΄ μμΌλ©΄ μ νν μ§νλ₯ νμμ€μ νμνκ³ μμ λ¨μ μκ°μ κ³μ°ν μ μμ΅λλ€. μ΄λ μ¬μ©μ κ²½νμ ν¬κ² ν₯μμν΅λλ€. μ΄ κ°μ μ»μΌλ €λ©΄ μ¬μ μ νμΌμ URLμ `HEAD` μμ²μ λ³΄λ΄ `Content-Length` ν€λλ₯Ό νμΈνκ±°λ, APIκ° λ©νλ°μ΄ν°μ μΌλΆλ‘ νμΌ ν¬κΈ°λ₯Ό μ 곡ν΄μΌ ν μ μμ΅λλ€.
λ¨μΌ νμΉμμ μ¬λ¬ νμΌ κ΄λ¦¬νκΈ°
μ΄ APIλ κ΄λ ¨ μμ°μ κ·Έλ£Ήνν λ λΉμ λ°ν©λλ€. μ¬μ©μκ° μ¬μ§ κ°€λ¬λ¦¬, λ¬Έμκ° ν¬ν¨λ μννΈμ¨μ΄ ν¨ν€μ§, λλ λͺ¨λ ν μ€μ²μ μ€λμ€ νμΌμ΄ ν¬ν¨λ λΉλμ€ κ²μ λ 벨μ λ€μ΄λ‘λνλ€κ³ μμν΄ λ³΄μμμ€. `backgroundFetch.fetch()`μ URL λ°°μ΄μ μ λ¬ν μ μμ΅λλ€. μ΄κ²μ λΈλΌμ°μ μ μν΄ νλμ μμμ μμ μΌλ‘ μ²λ¦¬λλ©°, μ 체 λ¬Άμμ λν΄ νλμ μλ¦Όκ³Ό νλμ μ§νλ₯ νμμ€μ΄ νμλ©λλ€. `backgroundfetchsuccess` νΈλ€λ¬μμ `bgFetch.matchAll()`μ λ μ½λ λ°°μ΄μ λ°ννλ©°, μ΄λ₯Ό κ°λ³μ μΌλ‘ μ²λ¦¬ν μ μμ΅λλ€.
μ€λ₯ μ²λ¦¬ λ° μ€ν¨ μλ리μ€
λ€μ΄λ‘λλ μ¬λ¬ κ°μ§ μ΄μ λ‘ μ€ν¨ν μ μμ΅λλ€. μλ²κ° 404 μ€λ₯λ₯Ό λ°ννκ±°λ, μ¬μ©μμ λμ€ν¬ 곡κ°μ΄ λΆμ‘±νκ±°λ, μ¬μ©μκ° λΈλΌμ°μ UIμμ μλμΌλ‘ λ€μ΄λ‘λλ₯Ό μ·¨μνλ κ²½μ°μ λλ€. `backgroundfetchfail` μ΄λ²€νΈ νΈλ€λ¬λ μμ λ§μ λλ€. μ΄λ₯Ό μ¬μ©νμ¬ λΆλΆ λ°μ΄ν°λ₯Ό μ 리νκ³ , μ ν리μΌμ΄μ λ΄μμ μ¬μ©μμκ² μλ¦¬κ³ , μ¬μλ λ²νΌμ μ 곡ν μ μμ΅λλ€. μ€ν¨κ° κ°λ₯μ±μ΄λΌλ κ²μ μ΄ν΄νλ κ²μ΄ κ²¬κ³ ν μμ€ν μ ꡬμΆνλ μ΄μ μ λλ€.
Cache APIλ‘ λ€μ΄λ‘λν μμ° μ μ₯νκΈ°
λ€μ΄λ‘λν μΉ μμ°μ μ μ₯νλ κ°μ₯ μΌλ°μ μ΄κ³ ν¨κ³Όμ μΈ μ₯μλ Cache APIμ λλ€. μ΄κ²μ `Request` λ° `Response` κ°μ²΄λ₯Ό μν΄ νΉλ³ν μ€κ³λ μ μ₯ λ©μ»€λμ¦μ λλ€. λ€μ΄λ‘λν νμΌμ μΊμμ μ μ₯ν¨μΌλ‘μ¨ λμ€μ μ¬μ©μκ° μ κ·Όνλ €κ³ ν λ μλΉμ€ μ컀μμ μ§μ μ 곡νμ¬ μ ν리μΌμ΄μ μ μ§μ μΌλ‘ μ€νλΌμΈμμ μ¬μ©ν μ μκ² λ§λ€ μ μμ΅λλ€.
λ€μν μ°μ λΆμΌμμμ μ¬μ© μ¬λ‘
λ°±κ·ΈλΌμ΄λ νμΉμ μμ© λΆμΌλ κ΄λ²μνλ©° μλ§μ κΈλ‘λ² μ°μ μ κ±Έμ³ μμ΅λλ€:
- λ―Έλμ΄ λ° μν°ν μΈλ¨ΌνΈ: μΉ κΈ°λ° μ€νΈλ¦¬λ° μλΉμ€λ μ€νλΌμΈ λͺ¨λλ₯Ό μ 곡νμ¬, μ΄λ€ κ΅κ°μ μ¬μ©μλ λ€μ΄ν°λΈ μ±μ²λΌ λΉνμ΄λ ν΅κ·Όμ μν΄ μνλ μμ μ λ€μ΄λ‘λν μ μκ² ν©λλ€.
- κ΅μ‘ λ° eλ¬λ: μν리카μ ν λνμ νμλ€μ΄ λμ©λ λΉλμ€ κ°μμ μνΈμμ©μ μΈ κ°μ μλ£λ₯Ό λ€μ΄λ‘λν μ μλ μΉ ν¬νΈμ μ 곡νμ¬, κ°μ μΈν°λ· νκ²½μ΄ μ’μ§ μμ νμλ€λ κ΅μ‘μ μ κ·Όν μ μλλ‘ λ³΄μ₯ν©λλ€.
- κΈ°μ λ° νμ₯ μλΉμ€: κΈλ‘λ² μ μ‘° νμ¬λ νμ₯ μμ§λμ΄μκ² PWAλ₯Ό μ 곡νμ¬, μΈν°λ· μ μμ΄ μλ μ격 νμ₯μΌλ‘ κ°κΈ° μ μ κ±°λν 3D λλ©΄κ³Ό κΈ°κ³ κΈ°μ λ§€λ΄μΌμ λ€μ΄λ‘λν μ μκ² ν©λλ€.
- μ¬ν λ° κ΄κ΄: μ¬ν μ ν리μΌμ΄μ μ μ¬μ©μκ° λͺ©μ μ§μ μ€νλΌμΈ μ§λ, λμ κ°μ΄λ, ν°μΌ μ 보λ₯Ό λ€μ΄λ‘λνμ¬ λΉμΌ κ΅μ λ°μ΄ν° λ‘λ° μκΈμ μ μ½ν μ μλλ‘ ν©λλ€.
λΈλΌμ°μ νΈνμ± λ° ν₯ν μ λ§
μ΄ κΈμ μ°λ μμ μμ λ°±κ·ΈλΌμ΄λ νμΉ APIλ μ£Όλ‘ Google Chrome λ° Microsoft Edgeμ κ°μ Chromium κΈ°λ° λΈλΌμ°μ μμ μ§μλ©λλ€. μ΅μ νΈνμ± μ 보λ CanIUse.comμ΄λ MDN Web Docsμ κ°μ μλ£λ₯Ό νμΈνλ κ²μ΄ μ€μν©λλ€. μμ§ λ³΄νΈμ μΌλ‘ μ±νλμ§λ μμμ§λ§, μ£Όμ λΈλΌμ°μ μμμ μ‘΄μ¬λ μ€μν μ§μ μ μλ―Έν©λλ€. μΉ νλ«νΌμ΄ κ³μ λ°μ ν¨μ λ°λΌ μ΄μ κ°μ APIλ μΉκ³Ό λ€μ΄ν°λΈ μ ν리μΌμ΄μ κ°μ κΈ°λ₯ 격차λ₯Ό μ€μ΄κ³ , κ°λ ₯νκ³ λ³΅μλ ₯ μμΌλ©° μ μΈκ³μ μΌλ‘ μ κ·Ό κ°λ₯ν μ°¨μΈλ PWAμ κΈΈμ μ΄μ΄μ£Όκ³ μμ΅λλ€.
κ²°λ‘ : λͺ¨λλ₯Ό μν λ 볡μλ ₯ μλ μΉ κ΅¬μΆ
λ°±κ·ΈλΌμ΄λ νμΉ APIλ λ¨μν νμΌμ λ€μ΄λ‘λνλ λꡬ μ΄μμ λλ€. κ·Έκ²μ μ°λ¦¬κ° λ§λ€κ³ μΆμ μΉμ μ’ λ₯μ λν μ μΈμ λλ€. μ¦, 볡μλ ₯ μκ³ , μ¬μ©μ μ€μ¬μ μ΄λ©°, κΈ°κΈ°λ λ€νΈμν¬ μ°κ²° νμ§μ κ΄κ³μμ΄ λͺ¨λ μ¬λμ μν΄ μλνλ μΉμ λλ€. λκ·λͺ¨ μ μ‘μ λΈλΌμ°μ μ μμν¨μΌλ‘μ¨ μ°λ¦¬λ μ¬μ©μλ₯Ό μ§νλ₯ νμμ€μ μ§μΌλ³΄λ λΆμκ°μμ ν΄λ°©μν€κ³ , κ·Έλ€μ λ°μ΄ν°μ λ°°ν°λ¦¬λ₯Ό μ μ½νλ©°, κ²¬κ³ νκ³ μ λ’°ν μ μλ κ²½νμ μ 곡ν©λλ€.
λμ©λ νμΌ μ μ‘μ ν¬ν¨νλ λ€μ μΉ νλ‘μ νΈλ₯Ό κ³νν λ, μ ν΅μ μΈ `fetch`λ₯Ό λμ΄μ μκ°νμμμ€. μ¬μ©μμ κΈλ‘λ² μ»¨ν μ€νΈλ₯Ό κ³ λ €νκ³ λ°±κ·ΈλΌμ΄λ νμΉμ νμ λ°μλ€μ¬ μ§μ μΌλ‘ νλμ μ΄κ³ μ€νλΌμΈ μ°μ μ μΈ μ ν리μΌμ΄μ μ ꡬμΆνμμμ€. μΉμ λ―Έλλ μ§μμ μ΄κ³ 볡μλ ₯ μμΌλ©°, μ΄μ λΉμ μ λ€μ΄λ‘λλ κ·Έλ΄ μ μμ΅λλ€.